package manager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import utils.CommonUtils;
import utils.FileUtils;
import utils.IOUtils;
import utils.UIUtils;

import bean.AppInfoBean;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.ResponseStream;
import com.lidroid.xutils.http.client.HttpRequest.HttpMethod;

import conf.constants.URLS;

import factory.ThreadPoolExecutorProxyFactory;

/**
 * 负责下载的逻辑 ,变成单例模式,提供状态DownloadInfo
 * 
 * @author Anson-pc
 * 
 */
public class DownLoadManager {

	public static final int STATE_UNDOWNLOAD = 0;
	public static final int STATE_DOWNLOADING = 1;
	public static final int STATE_PAUSEDOWNLOAD = 2;
	public static final int STATE_WAITINGDOWNLOAD = 3;
	public static final int STATE_DOWNLOADFAILED = 4;
	public static final int STATE_DOWNLOADED = 5;
	public static final int STATE_INSTALLED = 6;
	
	
	//用集合保存下载信息
	private Map<String,DownLoadInfo> mCacheDownLoadInfo = new HashMap<String,DownLoadInfo>();

	private static DownLoadManager instance;

	private DownLoadManager() {
	}

	public static DownLoadManager getInstance() {
		if (instance == null) {
			synchronized (DownLoadManager.class) {
				if (instance == null) {
					instance = new DownLoadManager();
				}
			}
		}
		return instance;
	}

	/**
	 * 异步开始下载数据
	 * 
	 * @call 用户点击了下载按钮被触发
	 * @param downloadInfo
	 */
	public void download(DownLoadInfo downloadInfo) {
		/*-----------  保存下载状态 ----------*/
		mCacheDownLoadInfo.put(downloadInfo.packageName, downloadInfo);
		
		
		downloadInfo.curState = STATE_UNDOWNLOAD;// 未下载
		// downloadInfo改变的时候,进行通知(发布消息)
		notifyObserver(downloadInfo);
		/**
		 * 预先把状态设置为等待状态 1.加入需要执行的下载任务<3个，需要执行的下载任务，应该会放到工作线程中
		 * 2.假如正在执行的下载任务》=3个，需要执行的下载任务，应该会放到"任务队列中"---》此时的状态还是等待中
		 */
		downloadInfo.curState = STATE_WAITINGDOWNLOAD;// 等待状态
		// downloadInfo改变的时候,进行通知(发布消息)
		notifyObserver(downloadInfo);
		ThreadPoolExecutorProxyFactory.getDownLoadPoolExecutorProxy().execute(
				new DownloadTask(downloadInfo));

	}

	class DownloadTask implements Runnable {

		private DownLoadInfo mDownloadInfo;

		public DownloadTask(DownLoadInfo downloadInfo) {
			mDownloadInfo = downloadInfo;
		}

		@Override
		public void run() {
			FileOutputStream out = null;
			InputStream in = null;
			try {
				long initRange = 0 ;
				
				File apkFile = new File(mDownloadInfo.savePath);
				
				if (apkFile.exists()) {
					initRange = apkFile.length();
				}
				
				//初始化当前进度
				mDownloadInfo.progress = initRange;
				
				mDownloadInfo.curState = STATE_DOWNLOADING;// 下载中
				// downloadInfo改变的时候,进行通知(发布消息)
				notifyObserver(mDownloadInfo);
				
				// 正在下载数据
				HttpUtils httpUtils = new HttpUtils();
				String url = URLS.BaseUrl + "download";
				// 请求的参数
				RequestParams params = new RequestParams();
				params.addQueryStringParameter("name",
						mDownloadInfo.downloadurl);
				params.addQueryStringParameter("range", ""+initRange);//修改初始化的range信息
				ResponseStream responseStream;

				responseStream = httpUtils
						.sendSync(HttpMethod.GET, url, params);
				
				boolean isPause = false ;

				// 得到数据
				if (responseStream.getStatusCode() == 200) {
					// 输入流
					
					in = responseStream.getBaseStream();
					// 保存路径
					String savePath = mDownloadInfo.savePath;
					File saveFile = new File(savePath);
					
					out = new FileOutputStream(saveFile,true);//追加写入

					// 对文件进行读写
					byte[] buffer = new byte[1024];
					int len = -1;
					while ((len = in.read(buffer)) != -1) {
						if (mDownloadInfo.curState == STATE_PAUSEDOWNLOAD) {
							isPause = true;
							break;//跳出循环
						}
						out.write(buffer, 0, len);
						
						mDownloadInfo.progress += len;
						
						mDownloadInfo.curState = STATE_DOWNLOADING;
						// downloadInfo改变的时候,进行通知(发布消息)
						notifyObserver(mDownloadInfo);
						
					}

					if (isPause) {//因为用户暂停下载，进来这里
						
					}else{
						// 下载完成了
						mDownloadInfo.curState = STATE_DOWNLOADED;
						// downloadInfo改变的时候,进行通知(发布消息)
						notifyObserver(mDownloadInfo);
						//移除缓存里面的状态
						mCacheDownLoadInfo.remove(mDownloadInfo.packageName);
					}
					
				} else {
					// 下载失败
					mDownloadInfo.curState = STATE_DOWNLOADFAILED;
					// downloadInfo改变的时候,进行通知(发布消息)
					notifyObserver(mDownloadInfo);
				}
			} catch (Exception e) {
				e.printStackTrace();
				mDownloadInfo.curState = STATE_DOWNLOADFAILED;
				// downloadInfo改变的时候,进行通知(发布消息)
				notifyObserver(mDownloadInfo);
			}finally{
				IOUtils.close(out);
				IOUtils.close(in);
			}

		}

	}

	/**
	 * 得到对应的DownLoadInfo信息，需要更新下载按钮的UI的时候
	 * 
	 * @param mAppInfoBean
	 */
	public DownLoadInfo getDownLoadInfo(AppInfoBean mAppInfoBean) {
		DownLoadInfo downLoadInfo = new DownLoadInfo();
		downLoadInfo.downloadurl = mAppInfoBean.downloadUrl;

		// apk存到什么目录，apk的命名规则
		String dir = FileUtils.getDir("apk");
		// apk命名规则
		String fileName = mAppInfoBean.packageName + ".apk";
		File saveFile = new File(dir, fileName);
		// 保存路径
		downLoadInfo.savePath = saveFile.getAbsolutePath();
		downLoadInfo.packageName = mAppInfoBean.packageName;
		
		downLoadInfo.max = mAppInfoBean.size;
		downLoadInfo.progress = 0;
		
		/*-----------  状态的值 ----------*/
		//已安装
		if (CommonUtils.isInstalled(UIUtils.getContext(), mAppInfoBean.packageName)) {
			downLoadInfo.curState = STATE_INSTALLED;
			return downLoadInfo;
			
		}
		
		/*-----------  下载完成 ----------*/
		File  apkFile = new File(downLoadInfo.savePath);
		if (apkFile.exists() && apkFile.length() == mAppInfoBean.size) {
			downLoadInfo.curState = STATE_DOWNLOADED;
			return downLoadInfo;
		}
		
		if (mCacheDownLoadInfo.containsKey(mAppInfoBean.packageName)) {
			return mCacheDownLoadInfo.get(mAppInfoBean.packageName);
		}
		
		/*--------- 未下载 ----------*/
		 downLoadInfo.curState = STATE_UNDOWNLOAD;
		 return downLoadInfo;
	}
	
	/*-----------  下面是观察者模式的方法 ----------*/
	/**
	 需要如下的3个步骤
	 1.AppDetailBottomHolder需要变成观察者
	 2.把观察者加到观察者集合中
	 3.数据(DownLoadInfo)改变的时候需要发布消息
	 */
	/**
	 * 定义接口以及接口方法
	 */
	public interface DownloadInfoObserver{
		void onDownLoadInfoChange(DownLoadInfo downLoadInfo);
	}
	
	/**
	 * 定义接口变量的集合
	 */
	List<DownloadInfoObserver> observers = new ArrayList<DownloadInfoObserver>();
	
	/*
	 * 添加观察者
	 */
	public void addObserver(DownloadInfoObserver o){
		observers.add(o);
	}
	
	/**
	 * 移除观察者
	 */
	public void deleteObserver(DownloadInfoObserver o){
		observers.remove(o);
	}
	
	/**
	 * 通知所有的观察者
	 */
	public void notifyObserver(DownLoadInfo downLoadInfo){
		//遍历集合
		for (int i = 0; i < observers.size(); i++) {
			observers.get(i).onDownLoadInfoChange(downLoadInfo);
		}
	}
	/*--------- end ----------*/

	/**
	 * 暂停下载
	 * @param downLoadInfo
	 */	
	public void pauseDownLoad(DownLoadInfo downLoadInfo) {
		/*-----------  当前状态：暂停 ----------*/
		downLoadInfo.curState = STATE_PAUSEDOWNLOAD;
		// downloadInfo改变的时候,进行通知(发布消息)
		notifyObserver(downLoadInfo);		
	}
}
